using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Vin.Extension.Framework.Models;
using Vin.Extension.Framework.Monitor.Extension;
using Vin.Extension.Framework.Monitor.ServiceInterface;
using Vin.Tool.AspNetCore.Singleton;
using Vin.Tool.Core.ExceptionCategory;
using Vin.Tool.Core.JsonCategory;
using Vin.Tool.Core.NetCategory;
using Vin.Tool.Domain.SystemModel;

namespace Vin.Extension.Framework.Monitor.Middleware;

public class RequestLogMiddleware(RequestDelegate next) : IVinAfterMiddlewareExtension
{
    public async Task InvokeAsync(HttpContext context)
    {
        var startTime = DateTime.Now;
        await next(context);
        var endTime = DateTime.Now;
        var costTime = (endTime - startTime).Milliseconds;
        try
        {
            await CheckIsExecuteRequestLog(context, costTime);
        }
        catch
        {
            // ignored
        }
    }

    private async Task CheckIsExecuteRequestLog(HttpContext context, int costTime)
    {
        if (!context.IsExecuteRequestLog())
        {
            var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
            var sysRequestLogService = VinApp.GetRequiredServiceByHttpContext<ISysRequestLogService>();
            var endpoint = context.GetEndpoint();
            var controllerDescriptor = endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>();
            var contextInfo = context.GetClientDetail();
            var contextInfoModel = new VinHttpContextInfoModel()
            {
                ControllerPath = controllerDescriptor?.ControllerTypeInfo.FullName,
                ActionPath = controllerDescriptor?.DisplayName,
                Url = context.Request.Path,
                Method = context.Request.Method,
                Header = context.Request.Headers,
                Params = context.Request.Query,
                Data = !context.Request.HasFormContentType
                    ? await context.ReadToJson()
                    : context.FormDictionary(),
                Curl = context.ToCurl(),
                Ip = contextInfo.Ip,
                Location = contextInfo.Location,
                Browser = contextInfo.Browser,
                Os = contextInfo.Os,
                UserAgent = contextInfo.UserAgent,
                Code = context.Response.StatusCode,
                Exception = exception?.ExceptionMessage()
            };

            if (context.Response.Body.CanRead)
            {
                context.Response.Body.Seek(0, SeekOrigin.Begin);
                var body = await new StreamReader(context.Response.Body).ReadToEndAsync();
                var data = body.ToJsonNode();
                if (data != null && data.GetValueKind() == JsonValueKind.Object)
                {
                    var code = data["code"]?.GetValue<int>();
                    if (code != null)
                    {
                        contextInfoModel.Code = code.Value;
                    }

                    contextInfoModel.Body = data;
                }
                else
                {
                    contextInfoModel.Body = body;
                }
            }

            await sysRequestLogService.PreSaveAsync(contextInfoModel, costTime, new List<string>());
        }
    }
}